using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.UI;
using UnityGameFramework.Runtime;

namespace LS.Input
{
    public sealed class InputComponent : GameFrameworkComponent
    {
        [SerializeField]
        private InputActionAsset m_InputActionAsset;
        
        [SerializeField]
        private bool m_MultiPlayer;

        // [SerializeField]
        // private bool m_TakeoverInputModule;
        
        // [SerializeField]
        // [TypeSearch(typeof(IInputHelper),label:"Input Helper")]
        // private string m_InputHelperName;
        
        private PlayerInput m_PlayerInput;
        private PlayerInputManager m_PlayerInputManager;
        
        private EventComponent m_EventComponent;
        private SettingComponent m_SettingComponent;
        
        private bool m_IsInitialized = false;
        
        private readonly string m_SaveName = "playerHotKey";
        
        private readonly Dictionary<string, LinkedList<Action<InputAction.CallbackContext>>> m_ActionsEvents = new Dictionary<string, LinkedList<Action<InputAction.CallbackContext>>>();

        public bool Available
        {
            get
            {
                return m_InputActionAsset != null;
            }
        }
        
        public InputActionAsset InputActionAsset
        {
            get
            {
                return m_InputActionAsset;
            }

            set
            {
                m_InputActionAsset = value;
            }
        }

        public PlayerInput PlayerInput
        {
            get
            {
                return m_PlayerInput;
            }
        }

        public PlayerInputManager PlayerInputManager
        {
            get
            {
                return m_PlayerInputManager;
            }
        }
        
        public bool MultiPlayer
        {
            get
            {
                return m_MultiPlayer;
            }
            set
            {
                if(m_IsInitialized && m_MultiPlayer != value)
                    UninitMultiPlayer();
                
                m_MultiPlayer = value;
                
                if(m_IsInitialized && value == true)
                    InitMultiPlayer();
            }
        }
        
        // public bool TakeoverInputModule
        // {
        //     get
        //     {
        //         return m_TakeoverInputModule;
        //     }
        //     set
        //     {
        //         if (m_IsInitialized && value != m_TakeoverInputModule)
        //         {
        //             if(m_PlayerInput != null)
        //                 m_PlayerInput.uiInputModule = value? EventSystem.current.currentInputModule as InputSystemUIInputModule : null;
        //         }
        //
        //         m_TakeoverInputModule = value;
        //     }
        // }
        
        private void Start()
        {
            m_SettingComponent = GameEntry.GetComponent<SettingComponent>();
            m_EventComponent =  GameEntry.GetComponent<EventComponent>();
        }


        public bool Init()
        {
            if (m_InputActionAsset == null)
            {
                Log.Error("Input action asset is invalid.");
                return false;
            }

            Clear();
            if (!gameObject.TryGetComponent<PlayerInput>(out m_PlayerInput))
            {
                m_PlayerInput = gameObject.AddComponent<PlayerInput>();
            }
            
            m_PlayerInput.actions = m_InputActionAsset;
            m_PlayerInput.notificationBehavior = PlayerNotifications.InvokeCSharpEvents;
            m_PlayerInput.onActionTriggered += ActionTriggeredHandler;
            m_PlayerInput.onControlsChanged += ControlsChangedHandler;
            m_PlayerInput.onDeviceRegained += DeviceRegainedHandler;
            m_PlayerInput.onDeviceLost += DevicesLostHandler;
            
            // if (TakeoverInputModule)
            // {
            //     var inputModule = EventSystem.current.currentInputModule;
            //     if(inputModule is InputSystemUIInputModule ismodule)
            //         m_PlayerInput.uiInputModule = ismodule;
            // }
            
            LoadInput();
            m_IsInitialized = true;
            return true;
        }

        private void InitMultiPlayer()
        {
            m_PlayerInputManager = gameObject.AddComponent<PlayerInputManager>();
            //TODO: 多玩家输入设置.
            
        }

        private void UninitMultiPlayer()
        {
            if (m_PlayerInputManager != null)
            {
                DestroyImmediate(m_PlayerInputManager);
                m_PlayerInputManager = null;
            }
        }
        
        private void Clear()
        {
            if (m_PlayerInput != null)
            {
                DestroyImmediate(m_PlayerInput);
                m_PlayerInput = null;
            }

            if (m_PlayerInputManager != null)
            {
                DestroyImmediate(m_PlayerInputManager);
                m_PlayerInputManager = null;
            }
        }

        public bool SaveInput()
        {
            if (m_InputActionAsset != null)
            {
                string inputData = m_InputActionAsset.SaveBindingOverridesAsJson();
                m_SettingComponent.SetString(m_SaveName, inputData);
                m_SettingComponent.Save();
                return true;
            }
            return false;
        }

        public bool LoadInput()
        {
            if (!m_SettingComponent.HasSetting(m_SaveName))
            {
                return false;
            }
            
            if (m_InputActionAsset != null)
            {
                string inputData = m_SettingComponent.GetString(m_SaveName);
                m_InputActionAsset.LoadBindingOverridesFromJson(inputData);
                return true;
            }
            return false;
        }

        public void ResetInputToDefault()
        {
            m_InputActionAsset.RemoveAllBindingOverrides();
        }

        public bool EnableMap(string mapName)
        {
            InputActionMap map = m_InputActionAsset.FindActionMap(mapName, false);
            if (map == null)
            {
                return false;
            }

            if (map.enabled)
            {
                return false;
            }
            map.Enable();
            return true;
        }
        
        public bool DisableMap(string mapName)
        {
            InputActionMap map = m_InputActionAsset.FindActionMap(mapName, false);
            if (map == null)
            {
                return false;
            }

            if (map.enabled)
            {
                return false;
            }
            
            map.Disable();
            return true;
        }

        public bool IsMapEnabled(string mapName)
        {
            InputActionMap map = m_InputActionAsset.FindActionMap(mapName, false);
            if (map == null)
            {
                return false;
            }

            return map.enabled;
        }
        
        public bool HasMap(string mapName)
        {
            return m_InputActionAsset?.FindActionMap(mapName, false) != null;
        }

        public void RegistActionEvent(string actionName, Action<InputAction.CallbackContext> eventHandler)
        {
            if (!m_ActionsEvents.TryGetValue(actionName, out LinkedList<Action<InputAction.CallbackContext>> actions))
            {
                actions = new  LinkedList<Action<InputAction.CallbackContext>>();
                m_ActionsEvents[actionName] = actions;
            }
            actions.AddLast(eventHandler);
        }
        
        public void UnregistActionEvent(string actionName, Action<InputAction.CallbackContext> eventHandler)
        {
            if (m_ActionsEvents.TryGetValue(actionName, out LinkedList<Action<InputAction.CallbackContext>> actions))
            {
                actions = new  LinkedList<Action<InputAction.CallbackContext>>();
                actions.Remove(eventHandler);
                if (actions.Count == 0)
                    m_ActionsEvents.Remove(actionName);
            }
        }

        public int GetActionEventCount(string actionName)
        {
            if (m_ActionsEvents.TryGetValue(actionName, out LinkedList<Action<InputAction.CallbackContext>> actions))
            {
                return actions.Count;
            }

            return 0;
        }
        
        public bool HasActionsEvent(string actionName, Action<InputAction.CallbackContext>  eventHandler)
        {
            if (m_ActionsEvents.TryGetValue(actionName, out LinkedList<Action<InputAction.CallbackContext>> actions))
            {
                return actions.Contains(eventHandler);
            }

            return false;
        }
        
        public bool HasActionsEvent(string actionName)
        {
            return m_ActionsEvents.ContainsKey(actionName);
        }
        
        public void ClearActionsEvent(string actionName)
        {
            m_ActionsEvents.Remove(actionName);
        }
        
        public void ClearActionsEvent()
        {
            m_ActionsEvents.Clear();
        }
        
        private void ActionTriggeredHandler(InputAction.CallbackContext context)
        {
            var arg = ActionTriggeredEventArgs.Create<ActionTriggeredEventArgs>();
            arg.Context = context;
            m_EventComponent.FireNow(this, arg);
            string actionName = context.action.name;
            if (m_ActionsEvents.TryGetValue(actionName,out var actionEvents))
            {
                foreach (var actionEvent in actionEvents)
                {
                    actionEvent?.Invoke(context);
                }
            }
        }
        
        private void DevicesLostHandler(PlayerInput input)
        {
            var arg = DevicesLostEventArgs.Create<DevicesLostEventArgs>();
            arg.PlayerInput = input;
            m_EventComponent.FireNow(this, arg);
        }
        
        private void DeviceRegainedHandler(PlayerInput input)
        {
            var arg = DevicesRegainedEventArgs.Create<DevicesRegainedEventArgs>();
            arg.PlayerInput = input;
            m_EventComponent.FireNow(this, arg);
        }
        
        private void ControlsChangedHandler(PlayerInput input)
        {
            var arg = ControlsChangedEventArgs.Create<ControlsChangedEventArgs>();
            arg.PlayerInput = input;
            m_EventComponent.FireNow(this, arg);
        }
        
    }
}
